/***
* ==++==
*
* Copyright (c) Microsoft Corporation.  All rights reserved.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* xstreambuf.h
*
* Asynchronous I/O: stream buffer implementation details
*
* We're going to some lengths to avoid exporting C++ class member functions and implementation details across
* module boundaries, and the factoring requires that we keep the implementation details away from the main header
* files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as
* possible.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once

#include "xxwin32.h"
#include <safeint.h>

#ifndef _ASYNCRTIMP
#ifdef _ASYNCRT_EXPORT
#define _ASYNCRTIMP __declspec(dllexport)
#else
#define _ASYNCRTIMP __declspec(dllimport)
#endif
#endif
namespace async
{
    typedef unsigned char byte;

    class basic_streambuf;
    class basic_fstreambuf;
    class basic_tcpstreambuf;
    class basic_tcp_listener;

    class fstreambuf;
    class tcpstreambuf;

namespace details
{
    /// <summary>
    /// A record containing the essential private data members of a file stream,
    /// in particular the parts that need to be shared between the public header
    /// file and the implementation in the implementation file.
    /// </summary>
    struct _file_info
    {
        _ASYNCRTIMP _file_info(std::ios_base::openmode mode) : 
            m_rdpos(0), 
            m_wrpos(0), 
            m_atend(false), 
            m_buffer(nullptr),
            m_bufoff(0), 
            m_bufsize(0),
            m_buffill(0),
            m_mode(mode)
        {
        }
            
        // Positional data

        size_t m_rdpos;
        size_t m_wrpos;
        bool   m_atend;

        // Input buffer

        char   *m_buffer;
        size_t m_bufoff;    // File position that the start of the buffer represents.
        msl::utilities::SafeInt<size_t> m_bufsize;   // Buffer allocated size.
        size_t m_buffill;   // Amount of file data actually in the buffer

        std::ios_base::openmode m_mode;

        actors::details::win32::reentrant_lock m_lock;
    };

    /// <summary>
    /// This interface provides the necessary callbacks for completion events.
    /// </summary>
    class _filestream_callback
    {
    public:
        virtual void on_opened(basic_streambuf *) { }
        virtual void on_closed(bool) { }
        virtual void on_completed(size_t) { }
    };

    /// <summary>
    /// This interface provides the necessary callbacks for listener connection events.
    /// </summary>
    class _listener_callback
    {
    public:
        virtual void on_listening(basic_tcp_listener *) { }
        virtual void on_accept(basic_tcp_listener *, basic_streambuf *) { }
        virtual void on_opened(bool) { }
        virtual void on_closed(bool) { }
    };

    /// <summary>
    /// Create a basic_fstreambuf instance.
    /// </summary>
    /// <param name="info">A file info record pointer</param>
    /// <returns>A pointer to an allocated instance of basic_fstreambuf</returns>
    /// <remarks>
    /// This factory is here so that we can keep the constructor private and have only one function
    /// be a friend of the basic_fstreambuf class
    /// </remarks>
    _ASYNCRTIMP basic_fstreambuf *_createfsb(details::_file_info *);

    /// <summary>
    /// A record containing the essential private data members of a tcp stream,
    /// in particular the parts that need to be shared between the public header
    /// file and the implementation in the implementation file.
    /// </summary>
    struct _tcp_info
    {
        _ASYNCRTIMP _tcp_info() { }
        virtual _ASYNCRTIMP ~_tcp_info() { }    
        actors::details::win32::reentrant_lock m_lock;
    };

    /// <summary>
    /// Create a basic_tcpstreambuf instance.
    /// </summary>
    /// <param name="info">A tcp info record pointer</param>
    /// <returns>A pointer to an allocated instance of basic_tcpstreambuf</returns>
    /// <remarks>
    /// This factory is here so that we can keep the constructor private and have only one function
    /// be a friend of the basic_tcpstreambuf class
    /// </remarks>
    _ASYNCRTIMP basic_tcpstreambuf *_createtcp(details::_tcp_info *);

    _ASYNCRTIMP basic_tcp_listener *_createtcplst(details::_tcp_info *);
}
}

extern "C" 
{
/// <summary>
/// Open a file and create a streambuf instance to represent it.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <param name="filename">The name of the file to open</param>
/// <param name="mode">A creation mode for the stream buffer</param>
/// <param name="prot">A file protection mode to use for the file stream</param>
/// <returns>True if the opening operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool _open_fsb_str(async::details::_filestream_callback *callback, const char *, std::ios_base::openmode, int);

/// <summary>
/// Open a file and create a streambuf instance to represent it.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <param name="filename">The name of the file to open</param>
/// <param name="mode">A creation mode for the stream buffer</param>
/// <param name="prot">A file protection mode to use for the file stream</param>
/// <returns>True if the opening operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully opened, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool _open_fsb_wstr(async::details::_filestream_callback *callback, const wchar_t *, std::ios_base::openmode, int);

/// <summary>
/// Close a file stream buffer.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the file has been opened.</param>
/// <returns>True if the closing operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the file will eventually be successfully closed, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool _close_fsb_nolock(async::details::_file_info **info, async::details::_filestream_callback *callback);
_ASYNCRTIMP bool _close_fsb(async::details::_file_info **info, async::details::_filestream_callback *callback);


/// <summary>
/// Write data from a buffer into the file stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
/// <param name="count">The size (in bytes) of the buffer</param>
/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t _aputn_fsb(async::details::_file_info *info, async::details::_filestream_callback *callback, const async::byte *ptr, size_t count);

/// <summary>
/// Write a single byte to the file stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t _aputc_fsb(async::details::_file_info *info, async::details::_filestream_callback *callback, async::byte ch);

/// <summary>
/// Read data from a file stream into a buffer
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
/// <param name="count">The size (in bytes) of the buffer</param>
/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t _agetn_fsb(async::details::_file_info *info, async::details::_filestream_callback *callback, async::byte *ptr, size_t count);

/// <summary>
/// Flush all buffered data to the underlying file.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <returns>True if the request was initiated</returns>
_ASYNCRTIMP bool _sync_fsb(async::details::_file_info *info, async::details::_filestream_callback *callback);

/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns>True if the request was initiated</returns>
_ASYNCRTIMP size_t _seekrdpos_fsb(async::details::_file_info *info, size_t pos);

/// <summary>
/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="pos">The new position (offset from the start) in the file stream</param>
/// <returns>True if the request was initiated</returns>
_ASYNCRTIMP size_t _seekwrpos_fsb(async::details::_file_info *info, size_t pos);


/// <summary>
/// Connect to a remote port and create a streambuf instance to represent the connection.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the connection has been made.</param>
/// <param name="hostname">The name of the host computer to connect to.</param>
/// <param name="service">The name of the service (port number) to connect to.</param>
/// <returns>True if the connection operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the connection will eventually be successfully opened, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool _connect_tcp_str(async::details::_filestream_callback *callback, const char *hostname, const char *service);

/// <summary>
/// Connect to a remote port and create a streambuf instance to represent the connection.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when the connection has been made.</param>
/// <param name="hostname">The name of the host computer to connect to.</param>
/// <param name="service">The name of the service (port number) to connect to.</param>
/// <returns>True if the connection operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the connection will eventually be successfully opened, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool _connect_tcp_wstr(async::details::_filestream_callback *callback, const wchar_t *hostname, const wchar_t *service);

/// <summary>
/// Close a TCP stream buffer.
/// </summary>
/// <param name="info">The TCP info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the connection has been closed.</param>
/// <returns>True if the closing operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the connection will eventually be successfully closed, just that the process was started.
/// </remarks>
_ASYNCRTIMP bool _close_tcp(async::details::_tcp_info **info, async::details::_filestream_callback *callback);

/// <summary>
/// Create a TCP connection listener.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when a new connection been made.</param>
/// <param name="hostname">The name of the host computer listen at.</param>
/// <param name="service">The name of the service (port number) to listen at.</param>
/// <returns>True if the connection operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the connection will eventually be successfully opened, just that the process was started.
/// The callback interface will be used repeatedly and should be deleted when on_closed() is called.
/// </remarks>
_ASYNCRTIMP bool _listen_tcp_str(async::details::_listener_callback *callback, const char *hostname, const char *service);

/// <summary>
/// Create a TCP connection listener.
/// </summary>
/// <param name="callback">A pointer to the callback interface to invoke when a new connection been made.</param>
/// <param name="hostname">The name of the host computer listen at.</param>
/// <param name="service">The name of the service (port number) to listen at.</param>
/// <returns>True if the connection operation could be initiated, false otherwise.</returns>
/// <remarks>
/// True does not signal that the connection will eventually be successfully opened, just that the process was started.
/// The callback interface will be used repeatedly and should be deleted when on_closed() is called.
/// </remarks>
_ASYNCRTIMP bool _listen_tcp_wstr(async::details::_listener_callback *callback, const wchar_t *hostname, const wchar_t *service);

_ASYNCRTIMP bool _open_listener(async::details::_tcp_info *info, async::details::_listener_callback *callback);

_ASYNCRTIMP bool _close_listener(async::details::_tcp_info **info, async::details::_filestream_callback *callback);

/// <summary>
/// Write data from a buffer into the TCP stream.
/// </summary>
/// <param name="info">The TCP info record of the stream</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
/// <param name="count">The size (in bytes) of the buffer</param>
/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t _aputn_tcp(async::details::_tcp_info *info, async::details::_filestream_callback *callback, const async::byte *ptr, size_t count);

/// <summary>
/// Write a single byte to the TCP stream.
/// </summary>
/// <param name="info">The TCP info record of the stream</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t _aputc_tcp(async::details::_tcp_info *info, async::details::_filestream_callback *callback, async::byte ch);

/// <summary>
/// Read data from a TCP stream into a buffer
/// </summary>
/// <param name="info">The TCP info record of the stream</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <param name="ptr">A pointer to a buffer where the data should be placed</param>
/// <param name="count">The size (in bytes) of the buffer</param>
/// <returns>0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer</returns>
_ASYNCRTIMP size_t _agetn_tcp(async::details::_tcp_info *info, async::details::_filestream_callback *callback, async::byte *ptr, size_t count);

/// <summary>
/// Flush all buffered data to the underlying file.
/// </summary>
/// <param name="info">The file info record of the file</param>
/// <param name="callback">A pointer to the callback interface to invoke when the write request is completed.</param>
/// <returns>True if the request was initiated</returns>
_ASYNCRTIMP bool _sync_tcp(async::details::_tcp_info *info, async::details::_filestream_callback *callback);
}
